package com.example.interceptor;

import com.example.utils.IpUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
 * 网站访问统计拦截器
 *
 * @auth shanyue
 * @date 2025年08月06日10:46
 */
@Component
public class VisitInterceptor implements HandlerInterceptor {

    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    /**
     * 请求处理前拦截方法
     * 实现用户访问量统计逻辑，判断是否为新用户
     * 并更新当日/总访问量
     *
     * @param request  当前请求对象
     * @param response 当前响应对象
     * @param handler  处理器对象
     * @return 是否放行
     */
    @Override
    public boolean preHandle(@NotNull HttpServletRequest request,
                             @NotNull HttpServletResponse response,
                             @NotNull Object handler) {
        try {
            //获取唯一标识，登录用户ID或客户端IP
            String userIdOrIp = getUserIdOrIp(request);

            //构造Redis中使用的key
            String today = LocalDate.now().toString();
            String uvKey = "uv:" + today;
            String visitTodayKey = "visit:" + today;
            String visitTotalKey = "visit:total";

            // 访问标识加入当日UV set中
            Long addedCount = redisTemplate.opsForSet().add(uvKey, userIdOrIp);

            // 如果为新访客即Set中不存在，更新访问量
            if (addedCount != null && addedCount > 0) {
                //当日访问量++
                redisTemplate.opsForValue().increment(visitTodayKey);
                //总访问++
                redisTemplate.opsForValue().increment(visitTotalKey);

                // 设置UV set的过期时间为当天剩余秒数
                LocalDateTime now = LocalDateTime.now();
                LocalDateTime tomorrow = LocalDate.now().plusDays(1).atStartOfDay();
                long secondsUntilTomorrow = ChronoUnit.SECONDS.between(now, tomorrow);

                redisTemplate.expire(uvKey, Duration.ofSeconds(secondsUntilTomorrow));
            }
        } catch (Exception e) {
            // 记录异常日志，避免影响正常访问
            e.printStackTrace();
        }

        return true;
    }

    /**
     * 获取用户唯一标识，优先使用登录用户的用户名，否则使用客户端IP
     *
     * @param request 请求对象
     * @return 用户名或IP地址
     */
    private String getUserIdOrIp(HttpServletRequest request) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated() &&
                !"anonymousUser".equals(authentication.getPrincipal())) {
            //登录用户
            return authentication.getName();
        } else {
            //用户IP
            return IpUtils.getClientIp(request);
        }
    }
}
